#include "TcpClient.h"
#include "TcpConnection.h"
#include <unistd.h>
#include <iostream>
using std::endl;
using std::cout;
#include "File.h"

TcpClient::TcpClient(string ip, int port)
:addr_(ip, port)
,epoll_()
{
}

void TcpClient::start()
{
    for (int i = 0;i < 1000; ++i)
    {
        connect();
        epoll_.loop();
    }
    while (1)
    {
        epoll_.loop();
    }
}

void TcpClient::connect()
{
    shared_ptr<Socket> sock_(new Socket);
    int fd = sock_->fd();
    int status = ::connect(fd, (sockaddr*)addr_.getAddr(), sizeof(sockaddr_in));
    if (status != 0 && errno != EINPROGRESS)
    {
        ::close(fd);
        exit(EXIT_FAILURE);
        return;
    }

    shared_ptr<TcpConnection> pconn(new TcpConnection(fd, addr_, epoll_));
    shared_ptr<Channel> pchannel(new Channel);
    epoll_.epollAdd(fd, &*pconn, pchannel);
    pchannel->onConnCb_ = std::bind(&TcpClient::OnConnectionCb, this, pconn);
    pchannel->onCloseCb_ = std::bind(&TcpClient::OnCloseCb, this, pconn);

    if (0 == status)
    {
        pconn->socktype_ = esock_connected;
        Socket::getPeerAddr(fd).printAddr();
        pchannel->onMessCb_ = std::bind(&TcpClient::OnMessageCb, this, pconn);

        pchannel->onConnCb_();
    }
    else
    {
        pconn->socktype_ = esock_connecting;
        pchannel->onMessCb_ = std::bind(&TcpClient::OnEINPROGRESS, this, pconn);
        epoll_.epollWrite(fd, &*pconn, true);
    }
    map_.insert(std::make_pair(fd, pconn));
    socks_.insert(std::make_pair(fd, sock_));
}


void TcpClient::OnConnectionCb(shared_ptr<TcpConnection> &pconn)
{
    /*
    string s("Hello World");
    pconn->sendTcpData(s);
    */
}

void TcpClient::OnMessageCb(shared_ptr<TcpConnection> &pconn)
{
    //cout << "TcpClient OnMessage" << endl;
	uint32_t events = pconn->events;
	bool evRead = events | EPOLLIN || events | EPOLLHUP;
	bool evWrite = events | EPOLLOUT;
	bool evErr = events | EPOLLERR;

	if (evRead)
	{
		pconn->readTcpData();
		queue<string> &queue = pconn->getReadQueue();
        /*
		while (!queue.empty())
		{
			string &tmp = queue.front();
            cout << tmp << endl;
			queue.pop();
		}
        */
       recvFile(pconn);
        if (!evWrite)
        {
            return;
        }
	}
	if (evWrite)
	{
        pconn->sendTcpQueue();
        return;
	}
	if (evErr)
	{
        OnCloseCb(pconn);
	}
}

void TcpClient::OnEINPROGRESS(shared_ptr<TcpConnection> &pconn)
{
    Socket &sock_ = pconn->sockfd_;
    int fd = sock_.fd();
    int error;
    socklen_t len = sizeof(error);
    int code = ::getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len);
    if (-1 == code && 0 != error)
    {
        pconn->epoll_.channels_[fd]->onCloseCb_();
    }
    else
    {
        epoll_.epollWrite(fd, &*pconn, false);
        pconn->socktype_ = esock_connected;
        Socket::getPeerAddr(fd).printAddr();
        pconn->epoll_.channels_[fd]->onMessCb_ = std::bind(&TcpClient::OnMessageCb, this, pconn);
        pconn->epoll_.channels_[fd]->onConnCb_();
    }
}

void TcpClient::OnCloseCb(shared_ptr<TcpConnection> &pconn)
{
    Socket &sock = pconn->sockfd_;
    int fd = sock.fd();
    epoll_.epollDel(fd);
    map_.erase(fd);
    socks_.erase(fd);
}

int main()
{
    TcpClient client("127.0.0.1", 8888);
    client.start();
    return 0;
}
